/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package RadioProp;

import java.io.IOException;
import java.io.File;
import java.util.Scanner;

/**
 *
 * @author Alwyn
 */
public class PropCurves {

    private String filePath=".";
    private String directorySeparator="";

    public static final int LAND=0;
    public static final int COLD_SEA=1;
    public static final int WARM_SEA=2;

    private final double [] heightColumns={10, 20, 37.5, 75, 150, 300, 600, 1200, 0};
    private final double [] distanceRows={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
        13, 14, 15, 16, 17, 18, 19, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70,
        75, 80, 85, 90, 95, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200,
        225, 250, 275, 300, 325, 350, 375, 400, 425, 450, 475, 500, 525, 550, 575,
        600, 625, 650, 675, 700, 725, 750, 775, 800, 825, 850, 875, 900, 925, 950,
        975, 1000 };

    private double [][][] propCurves=new double[24][][];

    private final int UPPER_LIMIT=0;
    private final int LOWER_LIMIT=1;

    private double dFrequency;
    private int iFreqLower, iFreqUpper;

    public PropCurves()
    {
        this(".");
    }

    public PropCurves(String filePath)
    {
        this.filePath=filePath;
        directorySeparator=System.getProperty("file.separator", "\\");
    }

    public void setFrequency(double frequency)
    {
        //Frequency is in MHz
        dFrequency=frequency;
        
        if(dFrequency<100)
            dFrequency=100;
        
        if(dFrequency>2000)
            dFrequency=2000;

        // If the frequency is spot on, then set limits to same frequency
        if(dFrequency==100 || dFrequency==600 || dFrequency==2000)
            iFreqLower=iFreqUpper=(int)dFrequency;
        
        if(frequency<600)
        {
            iFreqLower=100;
            iFreqUpper=600;
        }
        else
        {
            iFreqLower=600;
            iFreqUpper=2000;
        }
        
    }

    private int selectPropCurve(int frequency, int percentage, int surface)
    {
        int offset=0;
        
        switch(percentage)
        {
            case 1:
                offset++;
            case 10:
                offset++;
        }
        
        switch(surface)
        {
            case WARM_SEA:
                offset+=2;
            case COLD_SEA:
                offset+=3;
        }
        
        switch(frequency)
        {
            case 2000:
                offset+=8;
            case 600:
                offset+=8;
        }
        
        return(offset);
    }

    private double readPropCurve(int distRow, int heightCol, int freq, int perc, int surf)
    {
        int propcurve=selectPropCurve(freq,perc,surf);

        if(propCurves[propcurve]==null)
        {
            propCurves[propcurve]=readDataFile(freq,perc,surf);
        }

        return propCurves[propcurve][distRow][heightCol];
    }

    private double [][] readDataFile(int frequency, int percentage, int surface)
    {
        String filename=String.format("%d_%d_", frequency, percentage);
        
        switch(surface)
        {
            case LAND:
            default:
                filename+="land";
                break;
            case COLD_SEA:
                if(percentage<50)
                    filename+="c";
                filename+="sea";
                break;
            case WARM_SEA:
                if(percentage<50)
                    filename+="w";
                filename+="sea";
        }
        
        filename=filePath+directorySeparator+filename+".txt";
        
        double [][] ret=new double[distanceRows.length][heightColumns.length];

        try
        {
            Scanner fileScan = new Scanner (new File(filename));

            for(int y=0;y<distanceRows.length;y++)
                for(int x=0;x<heightColumns.length;x++)
                    ret[y][x]=fileScan.nextDouble();

            fileScan.close();
        }
        catch (IOException e)
        {
            // catch possible io errors from readLine()
            System.out.println("Uh oh, got an IOException error!");
            e.printStackTrace();
        }

        return (ret);
    }

    public double getFieldStrength(double distance, double height, double percentage, int surface)
    {
        /*
         * percentage in % (1-50) not 0.01 to 0.5
         * surface is either LAND, COLD_SEA or WARM_SEA
         */
        
        // Set up all the required curves to be obtained
        int [] freqCurves;
        if(iFreqLower==iFreqUpper)
            freqCurves=new int[]{iFreqLower};
        else
            freqCurves=new int[]{iFreqLower,iFreqUpper};

        int [] percentCurves;
        if(percentage<1)
            percentage=1;

        if(percentage>50)
            percentage=50;

        if(percentage==1 || percentage==10 || percentage==50)
            percentCurves=new int[]{(int)percentage};
        else
        {
            if(percentage<10)
                percentCurves=new int[]{1,10};
            else
                percentCurves=new int[]{10,50};
        }

        int dlidx=getRowIndex(distance,LOWER_LIMIT);
        int duidx=getRowIndex(distance,UPPER_LIMIT);

        int [] distanceCurves;
        if(dlidx==duidx)
            distanceCurves=new int[]{dlidx};
        else
            distanceCurves=new int[]{dlidx,duidx};
        
        int hlidx=getColumnIndex(height,LOWER_LIMIT);
        int huidx=getColumnIndex(height,UPPER_LIMIT);

        int [] heightCurves;
        if(hlidx==huidx)
            heightCurves=new int[]{hlidx};
        else
            heightCurves=new int[]{hlidx,huidx};

        // Now, obtain the relevant data into a suitably small array, and
        // interpolate if necessary
        double [][][][] workArray;

        workArray=new double[freqCurves.length][][][];
        for(int a=0;a<freqCurves.length;a++)
        {
            workArray[a]=new double[percentCurves.length][][];
            for(int b=0;b<percentCurves.length;b++)
            {
                workArray[a][b]=new double[distanceCurves.length][];
                for(int c=0;c<distanceCurves.length;c++)
                {
                    workArray[a][b][c]=new double[heightCurves.length];
                    for(int d=0;d<heightCurves.length;d++)
                    {
                        workArray[a][b][c][d]=readPropCurve(
                                distanceCurves[c],heightCurves[d],
                                freqCurves[a],percentCurves[b],surface);
                    }

                    if(heightCurves.length==2)
                    {
                        workArray[a][b][c][0]=MathsHelper.logInterpolate(
                                height, heightCurves[0], heightCurves[1],
                                workArray[a][b][c][0], workArray[a][b][c][1]);
                    }
                }

                if(distanceCurves.length==2)
                {
                    workArray[a][b][0][0]=MathsHelper.logInterpolate(
                            distance, distanceCurves[0], distanceCurves[1],
                            workArray[a][b][0][0], workArray[a][b][1][0]);
                }
            }

            if(percentCurves.length==2)
            {
                workArray[a][0][0][0]=MathsHelper.QInterpolate(
                        percentage, percentCurves[0], percentCurves[1],
                        workArray[a][0][0][0], workArray[a][1][0][0]);
            }
        }

        if(freqCurves.length==2)
        {
            workArray[0][0][0][0]=MathsHelper.logInterpolate(
                    dFrequency, freqCurves[0], freqCurves[1],
                    workArray[0][0][0][0], workArray[1][0][0][0]);
        }

        // End up with the required value in workArray[0][0][0][0]

        return workArray[0][0][0][0];
    }


    private int getRowIndex(double distance, int limit)
    {
        if(distance<1)
            distance=1;

        if(distance>1000)
            distance=1000;

        return getIndex(distanceRows,distance,limit);
    }

    private int getColumnIndex(double height, int limit)
    {
        // Return the last column
        if(height==0)
            return 8;
        
        if(height<10)
            height=10;
        
        if(height>1200)
            height=1200;
        
        return getIndex(heightColumns,height,limit);
    }

    private int getIndex(double [] array, double find, int limit)
    {
        if(array.length==0)
            return -1;
        
        for(int i=1; i<array.length; i++)
        {
            if(array[i-1]==find)
                return(i-1);

            if(array[i-1]<=find && array[i]>find)
                if(limit==UPPER_LIMIT)
                    return i;
                else
                    if(limit==LOWER_LIMIT)
                        return(i-1);
        }
        
        return -1;
    }

}
